package models

import (
	"ShopSupplierApi/models/mysql"
	"ShopSupplierApi/utils"
	"bytes"
	dbsql "database/sql"
	"time"
)

//OrderData 订单数据
type OrderData struct{}

//Orders 订单实例
var Orders *OrderData

func init() {
	Orders = &OrderData{}
}

//OrderType 订单类型
type OrderType int

const (
	OrderType_NoPay  OrderType = iota //待支付
	OrderType_Pay                     //已支付
	OrderType_Send                    //已发货
	OrderType_NoSend                  //待发货
	OrderType_Back                    //退款退货
	OrderType_Finsh                   //已完成
	OrderType_Cancel                  //已取消
)

//OrderTimeType 订单统计时间类型
type OrderTimeType int

const (
	OrderTimeType_All   OrderTimeType = iota //所有
	OrderTimeType_Year                       //年
	OrderTimeType_Month                      //月
	OrderTimeType_Day                        //日
)

//OrderDataType 订单统计类型
type OrderDataType int

const (
	OrderDataType_Order  OrderDataType = 1 //订单
	OrderDataType_Amount OrderDataType = 2 //金额
)

//PayStatus 订单支付状态
type PayStatus int

const (
	PayStatus_Pay      PayStatus = iota //已支付
	PayStatus_NoPay                     //待支付
	PayStatus_Back                      //全额退款
	PayStatus_PayThree                  //已付款至到担保方
	PayStatus_PayPart                   //部分支付
	PayStatus_BackPart                  //部分退款
)

//Order 订单对象
type Order struct {
	OrderID      *uint64  //订单号
	ShipName     *string  //收货人名称
	ShipMobile   *string  //收货人手机
	ShipTel      *string  //收货人电话
	ShipAddr     *string  //收货人地址
	PayStatus    *string  //支付状态
	ShipStatue   *string  //发货状态
	CreateTime   *int64   //创建时间
	LastModified *int64   //最后修改时间
	TotalAmount  *float64 //订单总额
	LoginAccount *string  //会员帐号
}

//OrderProduct 订单商品信息
type OrderProduct struct {
	ProductID   *uint64  //货品ID
	ProductName *string  //货品名称
	ProductBN   *string  //货品编号
	Price       *float64 //单价
	Amount      *float64 //总价
	Nums        *int     //购买数量
}

//OrderInfo 订单详情信息
type OrderInfo struct {
	OrderID      *uint64                //订单号
	FinalAmount  *float64               //订单总额
	TotalAmount  *float64               //商品总额
	PayStatus    *string                //付款状态
	ShipStatus   *string                //发货状态
	CreateTime   *uint64                //下单时间戳
	LastModified *uint64                //最后修改时间戳
	PayMent      *string                //支付方式
	ShippingID   *int                   //快递ID
	Shipping     *string                //快递名称
	MemberID     *uint64                //会员ID
	Status       *string                //订单状态
	ShipArea     *string                //收货区域
	ShipName     *string                //收货人
	Weight       *float32               //订单重量
	ShipAddr     *string                //收货地址
	ShipZip      *string                //收件人邮编
	ShipEmail    *string                //收件人邮箱
	ShipTel      *string                //收货人电话
	ShipTime     *string                //配送时间段
	ShipMobile   *string                //收货人手机
	Source       *string                //订单来源
	SupplierID   *uint64                //供应商ID
	MarkText     *string                //订单备注
	MakeType     *string                //订单备注图标
	ScoreU       *float32               //使用积分
	Discount     *float64               //优惠金额
	Currency     *string                //货币类型
	CostPayMent  *float64               //支付费用
	CostProtect  *float64               //保价费用
	TaxContent   *string                //发票内容
	TaxType      *string                //发票类型
	CostItem     *float64               //订单商品价格
	IsProtect    *string                //是否保价
	ScoreG       *float32               //可得积分
	Payed        *float64               //已支付金额
	CostFreight  *float64               //快递费用
	Memo         *string                //订单附言
	Member       map[string]interface{} //基本会员信息
}

//支付
var pay = " AND pay_status != \"0\" "

//待支付
var nopay = " AND pay_status = \"0\" "

//退款
var back = " AND (pay_status = \"4\" OR pay_status = \"5\") "

//排序
var orderby = " GROUP BY (a.order_id) ORDER BY a.createtime desc "

//String OrderType类型说明
func (c OrderType) String() string {
	switch c {
	case OrderType_Back:
		return "退款退货"
	case OrderType_Cancel:
		return "已取消"
	case OrderType_Finsh:
		return "已完成"
	case OrderType_NoPay:
		return "待支付"
	case OrderType_NoSend:
		return "待发货"
	case OrderType_Pay:
		return "已支付"
	case OrderType_Send:
		return "已发货"
	default:
		return "所有"
	}
}

//生成订单查询语句
//param t OrderType 订单类型
func createOrderSQL(t OrderType) string {
	buf := bytes.NewBufferString("SELECT order_id, ship_name,ship_mobile,ship_tel,ship_addr,pay_status,ship_status,a.createtime,last_modified,total_amount,login_account name ")
	buf.WriteString(" FROM lcf_b2c_orders a LEFT JOIN sdb_pam_members b ON a.member_id = b.member_id ")
	buf.WriteString(" WHERE 1=1  ")
	buf.WriteString(" AND a.createtime > ? AND a.createtime <= ? ") //某个时间段
	switch t {
	case OrderType_NoPay: //待支付
		buf.WriteString(" AND status !=\"dead\" ") //活动订单
		buf.WriteString(nopay)
	case OrderType_Pay: //支付
		buf.WriteString(" AND status !=\"dead\" ") //活动订单
		buf.WriteString(pay)
	case OrderType_Send: //已发货
		buf.WriteString(" AND status !=\"dead\" ") //活动订单
		buf.WriteString(" AND ship_status = \"1\" ")
	case OrderType_NoSend: //待发货
		buf.WriteString(" AND status !=\"dead\" ") //活动订单
		buf.WriteString(" AND ship_status = \"0\" ")
	case OrderType_Back: //退款
		buf.WriteString(back)
	case OrderType_Finsh: //已完成
		buf.WriteString(" AND status =\"finish\" ")
	case OrderType_Cancel:
		buf.WriteString(" AND status = \"dead\"")
	}
	return buf.String()
}

//OrderList 供应商获取订单列表
//Param supplierID uint64 供应商ID
//Param t   OrderType   订单类型
//Param starttime   int64 开始时间戳
//Param endtime int64 结束时间戳
func (*OrderData) OrderList(supplierID uint64, t OrderType, starttime int64, endtime int64, p *Page) []Order {
	sql := createOrderSQL(t) + " AND supplier_id = ? " + orderby
	var result []Order
	mysql.RowsCallbackResult(p.SQL(sql), func(rows *dbsql.Rows) {
		for rows.Next() {
			//order_id, ship_name,ship_mobile,ship_tel,ship_addr,pay_status,
			//ship_status,a.createtime,last_modified,total_amount,login_account name
			var o Order
			err := rows.Scan(&o.OrderID, &o.ShipName, &o.ShipMobile, &o.ShipTel, &o.ShipAddr,
				&o.PayStatus, &o.ShipStatue, &o.CreateTime, &o.LastModified, &o.TotalAmount, &o.LoginAccount)
			if err == nil {
				result = append(result, o)
			}
		}
		p.Rows = int64(len(result))
		p.CountRows(sql, starttime, endtime, supplierID)
	}, starttime, endtime, supplierID)
	return result
}

//OrderItem 获取订单商品信息
//Param supplierID uint64 供应商ID
//Param orderID uint64 订单号
func (*OrderData) OrderItem(supplierID uint64, orderID uint64) []OrderProduct {
	sql := "SELECT a.product_id,b.product_name,b.product_bn,a.price,a.amount,a.nums FROM sdb_b2c_order_items a LEFT JOIN lcf_supplier_products b ON a.product_id = b.product_id " +
		" WHERE a.order_id = ? AND b.supplier_id = ? "
	if supplierID > 0 {
		sql = "SELECT a.product_id,a.`name` product_name,a.bn product_bn,a.price,a.amount,a.nums " +
			" FROM sdb_b2c_order_items a WHERE a.order_id = ? AND 0 = ? "
	}
	var result []OrderProduct
	err := mysql.RowsCallbackResult(sql, func(rows *dbsql.Rows) {
		for rows.Next() {
			var p OrderProduct
			err := rows.Scan(&p.ProductID, &p.ProductName, &p.ProductBN, &p.Price, &p.Amount, &p.Nums)
			if err == nil {
				result = append(result, p)
			}
		}
	}, orderID, supplierID)
	if err != nil {
		utils.Logger.Error("订单商品查询失败", err)
	}
	return result
}

//OrderInfo 获取订单详情
//Param supplierID uint64 供应商ID
//Param orderID uint64 订单号
func (*OrderData) OrderInfo(supplierID uint64, orderID uint64) (*OrderInfo, error) {
	sql := "SELECT * FROM lcf_b2c_orders WHERE order_id = ? AND supplier_id = ? "
	row, err := mysql.Row(sql, orderID, supplierID)
	if err != nil {
		return nil, err
	}
	ord := &OrderInfo{}
	err = row.Scan(&ord.OrderID, &ord.FinalAmount, &ord.TotalAmount, &ord.PayStatus, &ord.ShipStatus, &ord.CreateTime, &ord.LastModified,
		&ord.PayMent, &ord.ShippingID, &ord.Shipping, &ord.MemberID, &ord.Status, &ord.ShipArea, &ord.ShipName,
		&ord.Weight, &ord.ShipAddr, &ord.ShipZip, &ord.ShipEmail, &ord.ShipTel, &ord.ShipTime, &ord.ShipMobile, &ord.Source, &ord.SupplierID,
		&ord.MarkText, &ord.MakeType, &ord.ScoreU, &ord.Discount, &ord.Currency, &ord.CostPayMent, &ord.CostProtect,
		&ord.TaxContent, &ord.TaxType, &ord.CostItem, &ord.IsProtect, &ord.ScoreG, &ord.Payed, &ord.CostFreight, &ord.Memo)
	if err != nil {
		return ord, err
	}
	ord.Member = SimpleMemberInfo(*ord.MemberID)
	return ord, nil
}

//ProductOrders 商品订单列表
//Param supplierID uint64 供应商ID
//Param ProductID uint64 货品ID
//Param p *Page 数据分页信息
func (*OrderData) ProductOrders(supplierID uint64, productID uint64, p *Page) []Order {
	sql := "SELECT a.order_id, ship_name, ship_mobile, ship_tel, ship_addr, pay_status, ship_status, a.createtime,last_modified,total_amount,login_account name" +
		" FROM sdb_b2c_order_items c LEFT JOIN lcf_b2c_orders a ON a.order_id = c.order_id LEFT JOIN sdb_pam_members b ON a.member_id = b.member_id WHERE pay_status = \"1\" AND c.product_id = ? " +
		" AND a.supplier_id = ? GROUP BY a.order_id "
	var result []Order
	err := mysql.RowsCallbackResult(p.SQL(sql), func(rows *dbsql.Rows) {
		for rows.Next() {
			var o Order
			err := rows.Scan(&o.OrderID, &o.ShipName, &o.ShipMobile, &o.ShipTel, &o.ShipAddr,
				&o.PayStatus, &o.ShipStatue, &o.CreateTime, &o.LastModified, &o.TotalAmount, &o.LoginAccount)
			if err == nil {
				result = append(result, o)
			}
		}
		p.Rows = int64(len(result))
		p.CountRows(sql)
	}, productID, supplierID)
	if err != nil {
		utils.Logger.Error("商品订单查询失败", err)
	}
	return result
}

//orderCountSQL 生成订单统计SQL
//Param timeType OrderTimeType 订单统计时间类型
//Param dataType OrderDataType 数据类型
//Param payStatus PayStatus 支付状态
func orderCountSQL(timeType OrderTimeType, dataType OrderDataType, payStatus PayStatus) string {
	selectSQL := bytes.NewBufferString("SELECT")
	var timeSQL string
	switch dataType {
	case OrderDataType_Amount:
		selectSQL.WriteString(" SUM(final_amount) nums, ")
	default:
		selectSQL.WriteString(" COUNT(1) nums, ")
	}
	switch timeType {
	case OrderTimeType_Year:
		timeSQL = " AND b.`year` = @year GROUP BY b.`month` "
		selectSQL.WriteString(" b.`month` tag  ")
	case OrderTimeType_Month:
		timeSQL = " AND b.`year` = @year AND b.`month` = @month GROUP BY b.`day` "
		selectSQL.WriteString(" b.`day` tag  ")
	case OrderTimeType_Day:
		timeSQL = " AND b.`year` = @year AND b.`month` = @month AND b.`day` = @day GROUP BY b.`hour` "
		selectSQL.WriteString(" b.`hour` tag  ")
	default:
		timeSQL = " GROUP BY b.`year` "
		selectSQL.WriteString(" b.`year` tag  ")
	}
	selectSQL.WriteString(" FROM lcf_b2c_orders a LEFT JOIN orders_extinfo b ON a.order_id = b.order_id ")
	selectSQL.WriteString(" WHERE 1 = 1  AND a.supplier_id = @supplier_id ")
	switch payStatus {
	case PayStatus_Pay: //支付订单
		selectSQL.WriteString(pay)
	case PayStatus_NoPay: //未支付订单
		selectSQL.WriteString(nopay)
	case PayStatus_Back: //退款订单
		selectSQL.WriteString(back)
	}
	selectSQL.WriteString(timeSQL)
	return selectSQL.String()
}

//keyNotContainsAdd 检测mp中是否存在索引Key，不存在则添加
func keyNotContainsAdd(mp map[string]map[string]uint, k string) {
	if _, ok := mp[k]; !ok {
		mp[k] = map[string]uint{}
	}
}

//OrderCount 订单统计
//Param supplierID uint64 供应商ID
//Param timeType OrderTimeType 订单统计时间类型
//Param dataType OrderDataType 数据类型
//Param t time.Time 统计时间
func (*OrderData) OrderCount(supplierID uint64, timeType OrderTimeType, dataType OrderDataType, t time.Time) map[string]map[string]uint {
	result := map[string]map[string]uint{}
	args := map[string]interface{}{
		"year":        t.Year(),
		"month":       t.Month(),
		"day":         t.Day(),
		"supplier_id": supplierID,
	}
	//支付订单
	sql := orderCountSQL(timeType, dataType, PayStatus_Pay)
	orderCountRowsCount(result, "pay", sql, args)
	//待支付
	sql = orderCountSQL(timeType, dataType, PayStatus_NoPay)
	orderCountRowsCount(result, "nopay", sql, args)
	//退款订单
	sql = orderCountSQL(timeType, dataType, PayStatus_Back)
	orderCountRowsCount(result, "back", sql, args)
	return result
}

//rowsCount 结果统计
//Param result map[uint]map[string]uint 统计结果
//Param tp string 统计类型
//Param sql string SQL
//Param args map[string]interface{} SQL参数
func orderCountRowsCount(result map[string]map[string]uint, tp string, sql string, args map[string]interface{}) {
	if result == nil {
		return
	}
	sql, params, _ := mysql.ParseSQL(sql, args)
	rows, err := mysql.Rows(sql, params...)
	if err != nil {
		return
	}
	for _, v := range rows {
		day, _ := mysql.String(v["tag"])
		nums, _ := mysql.Int(v["nums"])
		keyNotContainsAdd(result, day)
		result[day][tp] = uint(nums)
	}
}

//OrderTimeCount 下单时间段统计
//Param supplierID uint64 供应商ID
//Param timeType OrderTimeType 订单统计时间类型
//Param t time.Time 统计时间
func (*OrderData) OrderTimeCount(supplierID uint64, timeType OrderTimeType, t time.Time) map[string]uint {
	result := map[string]uint{}
	buf := bytes.NewBufferString(" SELECT count(1) nums,`hour` FROM lcf_b2c_orders a LEFT JOIN orders_extinfo b ON a.order_id = b.order_id WHERE a.supplier_id = @supplier_id ")
	switch timeType {
	case OrderTimeType_Year:
		buf.WriteString(" AND year = @year ")
	case OrderTimeType_Month:
		buf.WriteString(" AND year = @year  AND month = @month ")
	case OrderTimeType_Day:
		buf.WriteString(" AND year = @year  AND month = @month AND day = @day ")
	}
	buf.WriteString(" GROUP BY b.`hour` ")
	args := map[string]interface{}{
		"supplier_id": supplierID,
		"year":        t.Year(),
		"month":       t.Month(),
		"day":         t.Day(),
	}
	sql, params, _ := mysql.ParseSQL(buf.String(), args)
	rows, err := mysql.Rows(sql, params...)
	if err != nil {
		return result
	}
	for _, v := range rows {
		hour, err := mysql.String(v["hour"])
		if err != nil {
			continue
		}
		nums, _ := mysql.Int(v["nums"])
		result[hour] = uint(nums)
	}
	return result
}

//orderMemberCountSQL 订单会员数据统计
//Param timeType OrderTimeType 订单统计时间类型
//Param payStatus PayStatus 支付状态
func orderMemberCountSQL(timeType OrderTimeType, payStatus PayStatus) string {
	buf := bytes.NewBufferString(" SELECT COUNT(1) nums,tag FROM (SELECT b.` ")
	var timeSQL string
	switch timeType {
	case OrderTimeType_Year:
		timeSQL = " AND year = @year "
		buf.WriteString("month")
	case OrderTimeType_Month:
		timeSQL = " AND year = @year  AND month = @month "
		buf.WriteString("day")
	case OrderTimeType_Day:
		timeSQL = " AND year = @year  AND month = @month AND day = @day "
		buf.WriteString("hour")
	default:
		buf.WriteString("year")
	}
	buf.WriteString("` tag,a.member_id FROM ")
	buf.WriteString(" lcf_b2c_orders a LEFT JOIN orders_extinfo b ON a.order_id = b.order_id WHERE  a.supplier_id = @supplier_id ")
	switch payStatus {
	case PayStatus_Pay: //支付订单
		buf.WriteString(pay)
	case PayStatus_NoPay: //未支付订单
		buf.WriteString(nopay)
	case PayStatus_Back: //退款订单
		buf.WriteString(back)
	}
	buf.WriteString(timeSQL)
	buf.WriteString(" GROUP BY a.member_id) AS tmp GROUP BY tag ")
	return buf.String()
}

//OrderMemberCount 订单会员数据统计
//Param supplierID uint64 供应商ID
//Param timeType OrderTimeType 订单统计时间类型
//Param t time.Time 统计时间
func (*OrderData) OrderMemberCount(supplierID uint64, timeType OrderTimeType, t time.Time) map[string]map[string]uint {
	result := map[string]map[string]uint{}
	args := map[string]interface{}{
		"year":        t.Year(),
		"month":       t.Month(),
		"day":         t.Day(),
		"supplier_id": supplierID,
	}
	//支付订单
	sql := orderMemberCountSQL(timeType, PayStatus_Pay)
	orderCountRowsCount(result, "pay", sql, args)
	//待支付
	sql = orderMemberCountSQL(timeType, PayStatus_NoPay)
	orderCountRowsCount(result, "nopay", sql, args)
	//退款订单
	sql = orderMemberCountSQL(timeType, PayStatus_Back)
	orderCountRowsCount(result, "back", sql, args)
	return result
}

//orderLocalCountSQL 时间段内订单地区统计
//Param timeType OrderTimeType 订单统计时间类型
//Param payStatus PayStatus 支付状态
func orderLocalCountSQL(timeType OrderTimeType, payStatus PayStatus) string {
	buf := bytes.NewBufferString(" SELECT b.`")
	var timeSQL string
	switch timeType {
	case OrderTimeType_Year:
		timeSQL = " AND year = @year "
		buf.WriteString("month")
	case OrderTimeType_Month:
		timeSQL = " AND year = @year  AND month = @month "
		buf.WriteString("day")
	case OrderTimeType_Day:
		timeSQL = " AND year = @year  AND month = @month AND day = @day "
		buf.WriteString("hour")
	default:
		buf.WriteString("year")
	}
	buf.WriteString("` tag,count(1) nums,c.local_name tag FROM lcf_b2c_orders a LEFT JOIN orders_extinfo b ON a.order_id = b.order_id ")
	buf.WriteString(" LEFT JOIN sdb_ectools_regions c ON b.province = c.region_id WHERE a.supplier_id = @supplier_id ")
	switch payStatus {
	case PayStatus_Pay: //支付订单
		buf.WriteString(pay)
	case PayStatus_NoPay: //未支付订单
		buf.WriteString(nopay)
	case PayStatus_Back: //退款订单
		buf.WriteString(back)
	}
	buf.WriteString(timeSQL)
	buf.WriteString(" GROUP BY b.province ORDER BY nums DESC ")
	return buf.String()
}

//OrderLocalCount 订单地区统计
//Param supplierID uint64 供应商ID
//Param timeType OrderTimeType 订单统计时间类型
//Param t time.Time 统计时间
func (*OrderData) OrderLocalCount(supplierID uint64, timeType OrderTimeType, t time.Time) map[string]map[string]uint {
	result := map[string]map[string]uint{}
	args := map[string]interface{}{
		"year":        t.Year(),
		"month":       t.Month(),
		"day":         t.Day(),
		"supplier_id": supplierID,
	}
	//支付订单
	sql := orderLocalCountSQL(timeType, PayStatus_Pay)
	orderCountRowsCount(result, "pay", sql, args)
	//待支付
	sql = orderLocalCountSQL(timeType, PayStatus_NoPay)
	orderCountRowsCount(result, "nopay", sql, args)
	//退款订单
	sql = orderLocalCountSQL(timeType, PayStatus_Back)
	orderCountRowsCount(result, "back", sql, args)
	return result
}
